import processing.serial.*;
import ddf.minim.*;
import ddf.minim.analysis.*;
import processing.serial.*;

AudioPlayer song;
Minim minim;
BeatDetect beat;
GameOfLife game;

int cols = 9;
int rows = 14;
int blocksize = 25;
int lag = 30;
int counter = 0;

/* MODE enum def*/
final int NORMAL = 0;
final int EXPAND = 1;
final int SHRINK = 2;
final int INVERSE = 3;
int mode;
/////////////////////
/* STATE enum def*/
//final int FIRSTCONTACT = 0;
final int WAITING = 2;
final int SENDING = 3;
int pState = WAITING;
/////////////////////

String filename;

//Serial communication
Serial port;
String portname = Serial.list()[0];
int baudrate = 9600;

void setup() {
  size(cols*blocksize, rows*blocksize);
  minim = new Minim(this);
  mouseClicked();
  game = new GameOfLife(cols, rows, blocksize);

  String[] portList = Serial.list();
  for (int i = 0; i<portList.length;  i++) {
    println(portList[i]);
  }
  port = new Serial(this, portname, baudrate);
}

void draw() {
  beat.detect(song.mix);
  if (beat.isOnset(0)) {
    counter++;
    game.step(mode);
  }
  else if (beat.isSnare()|| beat.isHat()) {
    changeMode(10, 5, 5, 1);
    counter = 0;
  }

  if (game.isDone()|| counter>100) {
    game.initRandom(0.5);
    counter =0;
    //println("----------------reset-----------------");
  }
  switch(pState)  {
    case SENDING: 
      println("Sending frame"); 
      int[] frame = game.parseFrame();
      //int[] frame = {65535,65535,65535,65535,65535,65535,65535,65535,65535};  //for checking leds
      sendFrame(frame);
      pState = WAITING;
      break;
    case WAITING:
      println("Waiting for arduino");
      break;
      
  }

  game.drawGrid();
}



void serialEvent(Serial myPort) {
  char inChar = myPort.readChar();
  switch (pState)  {
    case WAITING:
      if (inChar == '?')    {
        myPort.clear();  
        pState = SENDING; 
      }
      else  {
        println("Received "+ inChar + " while WAITING");  // shouldn't receive anything except '?'
      }
      break;
    case SENDING:
      println("Received " + inChar + " while SENDING");  // shouldnt't receive anything while sending
      break;
      /*
    case RECEIVING:
      if (inChar =='!') {pState = SENDING; break;}
      print(inChar); break; */ // for debugging
    default:
      break;
  }
}

void sendFrame(int[] frame) {
  for (int i = 0; i<frame.length; i++) {
    //port.write(frame[i]);
    byte firstByte = byte(frame[i]>>8);
    byte secondByte = byte(frame[i]);
    port.write(firstByte);
    port.write(secondByte);
  
  }
  //println("Sending");
  //println(frame);
}

void changeMode(float pNor, float pExp, float pShr, float pInv) {
  float sum = pNor + pExp + pShr + pInv;
  float n = random(sum);
  if (n< sum - pInv - pShr - pExp) {
    mode = NORMAL;
  }
  else if (n < sum - pInv - pShr) {
    mode = EXPAND;
    //println("EXPAND");
  }
  else if (n < sum - pInv) {
    mode = SHRINK;
  }
  else {
    mode = INVERSE;
  }
}

void mouseClicked() {
  if (song!=null && song.isPlaying()) {
    song.pause();
  }

  filename = selectInput();
  if (filename!=null) {
    song = minim.loadFile(filename, 2048);
    song.play();
    beat = new BeatDetect(song.bufferSize(), song.sampleRate());
  }
  else {
    exit();
  }
}

void stop()
{
  // always close Minim audio classes when you are finished with them
  if (song!=null)  song.close();
  // always stop Minim before exiting
  minim.stop();
  super.stop();
}

